package com.example.guard_toollib;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.Arrays;

/**
 * Created by tys on 2018/2/4.
 */

//dex加密
//打包apk
public class Main {

    private static boolean isRelease = true;

    private static final String DEBUG = "debug";
    private static final String RELEASE = "release";


    private static final String ARR_PREFIX = "guard-corelibrary/build/outputs/aar/guard-corelibrary-";
    private static final String ARR_SUFFIX = ".aar";
    private static final String MY_DEX_DIRECTOR = "guard-corelibrary/build/outputs/temp";
    private static final String SRC_APK_PREFIX = "app/build/outputs/apk/";
    private static final String SRC_APK_SUFFIX = ".apk";
    private static final String DEX_PREFIX = "classes";
    private static final String DEX_SUFFIX = ".dex";

    /**
     * apk输出目录
     */
    private static final String DEST_DIRECTOR = "app/" + (isRelease ? RELEASE : DEBUG);
    /**
     * 输出apk前缀
     */
    private static String DEFAULT_APP_PREFIX = "app-guard-"; //app-debug.apk
    /**
     * 签名密匙路径
     */
    private static final String JKS_FILE = "guard-toollib/cer/mycer.jks";
    /***
     * 密匙标识名(密匙别名)
     */
    private static final String JKS_ALIAS = "tys";
    /**
     * 密匙密码
     */
    private static final String KEY_PASS = "tys@123";
    /**
     * 密匙库
     */
    private static final String KS_PASS = "tys@123";

    public static void main(String[] args) {

        /*android Library生成的是arr文件 执行gradle任务后生成*/
        //1.制作只含有解密部分的dex文件（后面替换dex）
        String aar = ARR_PREFIX + (isRelease ? RELEASE : DEBUG) + ARR_SUFFIX;
        File aarFile = new File(aar);
        File myDexFolder = new File(MY_DEX_DIRECTOR);
        myDex(aarFile, myDexFolder);

        //2.加密原apk中所有dex文件
        String apk = SRC_APK_PREFIX + (isRelease ? RELEASE : DEBUG) + "/app-" + (isRelease ? RELEASE : DEBUG) + SRC_APK_SUFFIX;
        File apkFie = new File(apk);

        File guardApkFolder = new File(DEST_DIRECTOR + "/app");
        encryptDex(apkFie, guardApkFolder);


        //4.把生成的classes.dex 放入 apk解压目录 在压缩成apk
        packageApp(myDexFolder, guardApkFolder);

    }

    /**
     * 加密apk中所有dex文件
     *
     * @param apkFile
     * @param destFolder 加密的dex文件的输出目录
     */
    private static void encryptDex(File apkFile, File destFolder) {
        System.out.println(" encrypt dex files src apk:" + apkFile.getAbsolutePath() + "......");
        ZipUtil.unZip(apkFile, destFolder);
        File[] files = destFolder.listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                return name.endsWith(".dex");
            }
        });
        if (files == null) {
            throw new RuntimeException("Failed list file:" + destFolder.getAbsolutePath());
        }
        Arrays.sort(files);
        AES.init(AES.DEFAULT_PWD);
        File temp = null;
        for (int i = files.length - 1; i >= 0; i--) {//class3.dex   class2.dex  class.dex
            File dex = files[i];
            try {
                byte[] bytes = AES.getBytes(dex); //整个文件 加密
                byte[] encrypt = AES.encrypt(bytes);
                if (encrypt != null) {
                    temp = File.createTempFile("temp-guard", ".dex", destFolder);
                    try (FileOutputStream fos = new FileOutputStream(temp)) {
                        fos.write(encrypt);
                        fos.flush();
                        fos.close();
                        dex.delete();
                        File destFile = new File(destFolder, DEX_PREFIX + (i + 2) + DEX_SUFFIX);
                        if (!temp.renameTo(destFile.getAbsoluteFile()))
                            throw new IOException("failed rename encrypt file  from:" + temp.getAbsolutePath()
                                    + " to:" + destFile.getAbsolutePath());
                    }
                } else {
                    //AES加密出问题了
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (temp != null)
                    temp.delete();
            }
        }
        System.out.println("encrypt dex finish");
    }


    /**
     * 打包apk
     * apk打包流程:
     * https://developer.android.google.cn/studio/publish/app-signing.html#signing-manually
     *
     * @param myDexFolder 生成的包含解密的dex文件的路径
     * @param appCode     加密dex后的路径
     */
    private static void packageApp(File myDexFolder, File appCode) {
        File myDexFile = new File(myDexFolder, "classes.dex");
        //把生成了classes.dex 放入 apk解压目录 在压缩成apk
        if (!myDexFile.renameTo(new File(appCode, "classes.dex"))) {
            throw new RuntimeException("Failed move classes.dex:" + myDexFile.getAbsolutePath());
        }
        System.out.println("start zip files......");
        //1.zip压缩
        File unsignedApk = new File(DEST_DIRECTOR, "app-zip.apk");
        ZipUtil.zip(appCode, unsignedApk);

        //2.zipalign 对齐
        System.out.println("start zipalign......");
        File zipalignApk = new File(DEST_DIRECTOR, "app-zipAligned.apk");
        ProcessBuilder zipalignPb = new ProcessBuilder();
        zipalignPb.command("cmd", "/c", "zipalign", "-f", "4", unsignedApk.getAbsolutePath(),
                zipalignApk.getAbsolutePath());

        Process process = null;
        try {
            process = zipalignPb.start();
            process.waitFor();
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
        if (process.exitValue() != 0) {
            throw new RuntimeException("zipalign error");
        }
        System.out.println("zipalign finish");

        //3.apksigner 使用您的私钥签署 APK：
        System.out.println("start apksigner......");
        ProcessBuilder signedPb = new ProcessBuilder();
        File outFile = new File(DEST_DIRECTOR, DEFAULT_APP_PREFIX + (isRelease ? RELEASE : DEBUG) + ".apk");
        signedPb.command("cmd", "/c", "apksigner", "sign", "--ks", JKS_FILE, "--ks-key-alias", JKS_ALIAS,
                "--ks-pass", "pass:" + KS_PASS, "--key-pass", "pass:" + KEY_PASS, "--out",
                outFile.getAbsolutePath(), zipalignApk.getAbsolutePath());
        try {
            process = signedPb.start();
            process.waitFor();
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
        if (process.exitValue() != 0) {
            throw new RuntimeException("signer error");
        }
        System.out.println("apksigner finish");

        unsignedApk.delete(); //压缩后的apk
        zipalignApk.delete(); //对齐后的apk
        System.out.println("guard apk outputs:" + outFile.getAbsoluteFile());
    }

    /**
     * 制作只含有解密部分的dex文件
     *
     * @param aarFile
     * @param dexFolder
     */
    private static void myDex(File aarFile, File dexFolder) {
        System.out.println("created dex file......");
        File aarTemp = new File("guard-toollib", "temp");
        ZipUtil.unZip(aarFile, aarTemp);
        File classesJar = new File(aarTemp, "classes.jar");
        File classesDex = new File(dexFolder, "classes.dex");
        if (!dexFolder.exists()) {
            if (!dexFolder.mkdirs())
                throw new RuntimeException("Failed created folder:" + dexFolder.getAbsolutePath());
        } else {
            ZipUtil.cleanDirectory(dexFolder);
        }
        //执行命令  windows:cmd /c  linux/mac不需要（cmd /c）
        Process process = null;
        try {
            //1.2 执行dx命令 将jar变成dex文件
            ProcessBuilder pb = new ProcessBuilder();
            //D:\AndroidSdk\build-tools\27.0.1\dx.bat
            pb.command("cmd", "/c", "dx", "--dex", "--output", classesDex.getAbsolutePath(), classesJar.getAbsolutePath());
            process = pb.start();
            process.waitFor();
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
        //失败
        if (process.exitValue() != 0) {
            throw new RuntimeException("failed created dex :" + process.exitValue());
        }
        System.out.println("created dex finish");
    }



}
